package gateway

import (
	"context"
	"github.com/centrifugal/centrifuge"
	"github.com/liserc/opensocket-server/tool/discovery"
	"github.com/liserc/opensocket-server/tool/errs"
	"github.com/liserc/opensocket-server/tool/stringutil"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

type WebServer interface {
	Run(done chan error) error
	SetDiscoveryRegistry(client discovery.SvcDiscoveryRegistry, config *Config)
	GetUserAllCons(userID string) ([]*centrifuge.Client, bool)
	GetUserPlatformCons(userID string, platform int) ([]*centrifuge.Client, bool, bool)
}

type CentrifugeServer struct {
	config   *Config
	registry discovery.SvcDiscoveryRegistry
	connect  *Handler
}

func NewWebServer(conf *Config) *CentrifugeServer {
	return &CentrifugeServer{
		config: conf,
	}
}

func (ws *CentrifugeServer) Run(done chan error) error {
	var (
		netErr error
	)

	cfg := centrifuge.Config{}
	node, err := centrifuge.New(cfg)
	if err != nil {
		netErr = errs.WrapMsg(err, "centrifuge node err", err)
		return netErr
	}

	clientHandle := NewHandler(node)
	err = clientHandle.Setup()
	if err != nil {
		netErr = errs.WrapMsg(err, "centrifuge client err", err)
		return netErr
	}

	websocketHandler := centrifuge.NewWebsocketHandler(node, websocketHandlerConfig())
	ws.connect = clientHandle

	httpServer := http.Server{Addr: ":" + stringutil.IntToString(ws.config.Gateway.Websocket.Port), Handler: nil}
	netDone := make(chan struct{}, 1)
	go func() {
		http.Handle("/connection/websocket", websocketHandler)
		err = httpServer.ListenAndServe()
		defer close(netDone)
		if err != nil && err != http.ErrServerClosed {
			netErr = errs.WrapMsg(err, "websocket start err", httpServer.Addr)
		}
	}()

	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	select {
	case err = <-done:
		nErr := node.Shutdown(context.Background())
		if nErr != nil {
			return errs.WrapMsg(nErr, "node shutdown err")
		}
		sErr := httpServer.Shutdown(ctx)
		if sErr != nil {
			return errs.WrapMsg(sErr, "server shutdown err")
		}
		if err != nil {
			return err
		}
	case <-netDone:
	case <-sigCh:
		done <- errs.New("syscall shutdown")
	}

	return netErr
}

func (ws *CentrifugeServer) SetDiscoveryRegistry(registry discovery.SvcDiscoveryRegistry, config *Config) {
	var messageHandle = NewGrpcMessageHandle(registry, &config.Share.RpcRegisterName)
	ws.connect.SetMessageHandle(messageHandle)
	ws.registry = registry
}

func (ws *CentrifugeServer) GetUserAllCons(userID string) ([]*centrifuge.Client, bool) {
	connectMap := ws.connect.node.Hub().UserConnections(userID)
	connectList := make([]*centrifuge.Client, len(connectMap))
	for _, v := range connectMap {
		connectList = append(connectList, v)
	}
	return connectList, len(connectList) > 0
}

func (ws *CentrifugeServer) GetUserPlatformCons(userID string, platform int) ([]*centrifuge.Client, bool, bool) {
	return nil, false, false
}

func websocketHandlerConfig() centrifuge.WebsocketConfig {
	cfg := centrifuge.WebsocketConfig{}
	cfg.CheckOrigin = getCheckOrigin()
	cfg.PingPongConfig = getPingPongConfig()
	return cfg
}

func getCheckOrigin() func(r *http.Request) bool {
	return func(r *http.Request) bool {
		return true
	}
}

func getPingPongConfig() centrifuge.PingPongConfig {
	return centrifuge.PingPongConfig{
		PingInterval: 5 * time.Second,
		PongTimeout:  5 * time.Second,
	}
}
